require 'weapon'
require 'particle'
require 'hud'
require 'ai'

class Unit
  attr_reader :x, :y, :z, :dx, :dy, :weapon_angle, :hud_orientation, :direction, :width, :score, :color_uniform, :color_skin, :health, :dead, :gibbed, :final_revenge, :unit_index, :team, :poisoned
  DEFAULT_WIDTH = 16
  DEFAULT_HEIGHT = 40
  GIB_ANIM_INTERVAL = 150
  def initialize(window, color_uniform, color_skin, team, unit_index, controller)
    @window, @color_uniform, @color_skin, @unit_index, @team, @controller = window, color_uniform, color_skin, unit_index, team, controller
    @original_color_skin = @color_skin
    @score = 0
    if ["Human (left)", "Human (right)"].include?(@controller)
      @hud_orientation = :left if @controller == "Human (left)"
      @hud_orientation = :right if @controller == "Human (right)"
      hud_x, hud_y = 16, 16 if @hud_orientation == :left
      hud_x, hud_y = @window.width - 16, 16 if @hud_orientation == :right
      @hud = HUD.new(window, self, hud_x, hud_y)
      @human = true
    else
      @human = false
      @ai = AI.new(self)
    end
    
    @scale, @speed, @death_splash_radius, @dead = 1, 1.5, 60, false
    @dx, @dy, @z, @angle = 0, 0, 1, 0
    @selected_weapon_index = 0
    @anim_uniform = Anims["media/unit_uniform.png"]
    @anim_skin = Anims["media/unit_skin.png"]
    @anim_damage1 = Anims["media/unit_damage1.png"]
    @anim_damage2 = Anims["media/unit_damage2.png"]
    @anim_damage3 = Anims["media/unit_damage3.png"]
    @anim_damage4 = Anims["media/unit_damage4.png"]
    @anim_damage5 = Anims["media/unit_damage5.png"]
    @anim_damage6 = Anims["media/unit_damage6.png"]
    
    @img_uniform_standing = @anim_uniform[0]
    @img_uniform_dead = @anim_uniform[2]
    @anim_uniform_walking = @anim_uniform[0..1]
    @anim_uniform_gibbing = @anim_skin[3..5]
    @img_skin_standing = @anim_skin[0]
    @img_skin_dead = @anim_skin[2]
    @anim_skin_walking = @anim_skin[0..1]
    @anim_skin_gibbing = @anim_skin[3..5]
    
    @img_damage_standing = []
    @anim_damage_walking = []
    @img_damage_standing[1] = @anim_damage1[0]
    @anim_damage_walking[1] = @anim_damage1[0..1]
    
    @img_damage_standing[2] = @anim_damage2[0]
    @anim_damage_walking[2] = @anim_damage2[0..1]
    
    @img_damage_standing[3] = @anim_damage3[0]
    @anim_damage_walking[3] = @anim_damage3[0..1]
    
    @img_damage_standing[4] = @anim_damage4[0]
    @anim_damage_walking[4] = @anim_damage4[0..1]
    
    @img_damage_standing[5] = @anim_damage5[0]
    @anim_damage_walking[5] = @anim_damage5[0..1]
    
    @img_damage_standing[6] = @anim_damage6[0]
    @anim_damage_walking[6] = @anim_damage6[0..1]
    
    @cur_skin_image = @img_skin_standing
    @cur_uniform_image = @img_uniform_standing
    @cur_damage_image = nil
  end
  def place(map, x, y, direction, width = 0, height = 0)
    @lifetime = 0
    @final_revenge = false
    @poisoned = false
    @color_skin = @original_color_skin
    @die_sound = "media/die#{rand(3) + 1}.wav"
    @map, @x, @y, @direction, @width, @height = map, x, y, direction, width, height
    #if you had perfect health last round, you get the poison grenade launcher
    if @health and GameConfig["unit#{@unit_index}_health"] == @health
      @weapons = [PoisonGrenadeLauncher.new(@window, self), Rifle.new(@window, self), Grenades.new(@window, self)]
    else
      @weapons = [GrenadeLauncher.new(@window, self), Rifle.new(@window, self), Grenades.new(@window, self)]
    end
    @health, @dead, @gibbed, @angle = (GameConfig["unit#{@unit_index}_health"] || 100).to_i, false, false, 0
    @width = DEFAULT_WIDTH if @width == 0
    @height = DEFAULT_HEIGHT if @height == 0
    @dead = false
    @reload_timer = 0
    @teammates = $game.units.reject { |u| u.team != @team }
    
    case @direction
      when :left
        @weapon_angle = -90
      when :right
        @weapon_angle = 90
    end
    @ai.new_round unless @human
  end
  def draw
    factor = 1
    factor = -1 if @direction == :left
    selected_weapon.draw
    offset = @direction == :right ? 0 : @cur_uniform_image.width
    if !(@gibbed and milliseconds - @gib_start > GIB_ANIM_INTERVAL * @anim_skin_gibbing.size)
      if @dead
        y_adjust = Math.sin(angle_to_radians(@angle)).abs * @height/2
        @cur_skin_image.draw_rot(@x + offset, @window.height - @y + y_adjust, ZOrder::Units, @angle, 0.5, 0.5, factor, 1.0, @color_skin)
        @cur_uniform_image.draw_rot(@x + offset, @window.height - @y + y_adjust, ZOrder::Units, @angle, 0.5, 0.5, factor, 1.0, @color_uniform)
        if @cur_damage_image
          @cur_damage_image.draw_rot(@x + offset, @window.height - @y + y_adjust, ZOrder::Units, @angle, 0.5, 0.5, factor)
        end
      else
        @cur_skin_image.draw(@x - @width / 2 + offset, @window.height - @y - @height / 2, ZOrder::Units, factor, 1.0, @color_skin)
        @cur_uniform_image.draw(@x - @width / 2 + offset, @window.height - @y - @height / 2, ZOrder::Units, factor, 1.0, @color_uniform)
        if @cur_damage_image
          @cur_damage_image.draw(@x - @width / 2 + offset, @window.height - @y - @height / 2, ZOrder::Units, factor)
        end
      end
    end
    #$game.draw_quad(@x - 1, @window.height - (@y - 1), 0xffffffff, @x + 1, @window.height - (@y - 1), 0xffffffff, @x - 1, @window.height - (@y + 1), 0xffffffff, @x + 1, @window.height - (@y + 1), 0xffffffff, 999)
    
    @hud.draw if @hud
  end
  def do_timer
    $game.play(@reload_sound) if @reload_timer == 1
    @reload_timer -= 1 if @reload_timer > 0
  end
  def take_damage(unit, damage, x = 0, y = 0, poison = false)
    DebugOut.out "unit #{@unit_index} took #{damage} damage."
    x = @x if x == 0
    y = @y if y == 0
    if poison
      @poisoned = true
      @color_skin = 0xff007f00
      @poisoner = unit
    end
    ((damage / 10) + 1).to_i.times do
      Particle.new(x, y, 'media/blood.bmp', -2.0+rand*4.0,-2.0+rand*4.0, rand*360, 0, 1.0, 0.05, 10, :default)
    end
    @health -= damage.to_i
    if @health > 0
      $game.play(Sounds["media/ouch#{rand(3) + 1}.wav"]) if damage > 1
      if @health <= 10 and !@final_revenge
        teammate_alive = false
        @teammates.each { |t| teammate_alive = true if !t.dead and t != self }
        
        if !teammate_alive
          @final_revenge = true
          $game.play("media/final_revenge.wav")
        end
      end
    else
      if health <= -50 and !@gibbed
        @gibbed = true
        @gib_start = milliseconds
        (rand(4) + 4).times do
          Giblet.new(@window, self, @x, @y, rand(90)-45, rand(5)+2)
        end
        10.times do
          Particle.new(x, y, 'media/blood.bmp', -2.0+rand*4.0,-2.0+rand*4.0, rand*360, 0, 1.0, 0.05, 10, :default)
        end
      end
      die(unit) if !@dead
    end
    
    @health < 1
  end
  def die(unit)
    @dead = true
    $game.play(Sounds[@die_sound])
    if @final_revenge
      ClusterGrenade.new(@window, self, @x, @y, @weapon_angle, 0xffffffff, 10)
    end
    if unit == self
      score_change(-1)
    else
      unit.score_change(1)
    end
    @color = 0xff555555
    selected_weapon.unit_died
    @window.unit_died
  end
  def fire
    unless @dead
      if selected_weapon.fire
        @dx -= offset_x(@weapon_angle, selected_weapon.recoil)
      end
    end
    nil
  end
  
  def switch_weapon(dir)
    @selected_weapon_index += dir
    @selected_weapon_index = @weapons.size - 1 if @selected_weapon_index < 0
    @selected_weapon_index = 0 if @selected_weapon_index > @weapons.size - 1
  end
  
  def ai_update
    update(*@ai.get_update)
  end
  
  def notify_landing(missile)
    @ai.notify_landing(missile) unless @human
  end
  def notify_off_screen(missile)
    @ai.notify_off_screen(missile) unless @human
  end
  def notify_reloading
    @ai.notify_reloading unless @human
  end
  def notify_hit(victim)
    @ai.notify_hit(victim) unless @human
  end
  
  def update(move_x, weapon_angle_adjust, force_adjust)
    @lifetime += 1
    @dy += Game::GRAVITY
    #adjust y
    prev_y = @y
    @dy.abs.to_i.times do
      if @map.would_fit(@x, @y-1, @width, @height)
        @y -= @dy <=> 0
      else @dy = 0
      end
    end
    
    if move_x == 0 or @dead
      @cur_skin_image = @img_skin_standing
      @cur_uniform_image = @img_uniform_standing
      if health > 95
        @cur_damage_image = nil
      elsif health > 80
        @cur_damage_image = @img_damage_standing[1]
      elsif health > 60
        @cur_damage_image = @img_damage_standing[2]
      elsif health > 40
        @cur_damage_image = @img_damage_standing[3]
      elsif health > 20
        @cur_damage_image = @img_damage_standing[4]
      elsif health > 0
        @cur_damage_image = @img_damage_standing[5]
      elsif !@gibbed
        @cur_damage_image = @img_damage_standing[6]
      end
    else
      @cur_skin_image = (milliseconds / 175 % 2 == 0) ? @anim_skin_walking[0] : @anim_skin_walking[1]
      @cur_uniform_image = (milliseconds / 175 % 2 == 0) ? @anim_uniform_walking[0] : @anim_uniform_walking[1]
      if health > 95
        @cur_damage_image = nil
      elsif health > 80
        @cur_damage_image = (milliseconds / 175 % 2 == 0) ? @anim_damage_walking[1][0] : @anim_damage_walking[1][1]
      elsif health > 60
        @cur_damage_image = (milliseconds / 175 % 2 == 0) ? @anim_damage_walking[2][0] : @anim_damage_walking[2][1]
      elsif health > 40
        @cur_damage_image = (milliseconds / 175 % 2 == 0) ? @anim_damage_walking[3][0] : @anim_damage_walking[3][1]
      elsif health > 20
        @cur_damage_image = (milliseconds / 175 % 2 == 0) ? @anim_damage_walking[4][0] : @anim_damage_walking[4][1]
      elsif health > 0
        @cur_damage_image = (milliseconds / 175 % 2 == 0) ? @anim_damage_walking[5][0] : @anim_damage_walking[5][1]
      elsif !@gibbed
        @cur_damage_image = (milliseconds / 175 % 2 == 0) ? @anim_damage_walking[6][0] : @anim_damage_walking[6][1]
      end
      unless @dead
        #move unless we're already being thrown about
        @dx += move_x * @speed unless (move_x * @speed).abs < @dx.abs
      end
    end
    
    @cur_skin_image = @img_skin_dead if @dead
    @cur_uniform_image = @img_uniform_dead if @dead
    
    if @dx < 0 and move_x < 0 and @direction == :right
      @direction = :left
      @weapon_angle = -@weapon_angle
    elsif @dx > 0 and move_x > 0 and @direction == :left
      @direction = :right
      @weapon_angle = -@weapon_angle
    end
    
    @dx.to_i.abs.times do
      ydiff = @y
      until @map.would_fit(@x + (@dx <=> 0), ydiff-1, @width, @height)
        ydiff += 1
      end
      if ydiff - y < @height
        @y = ydiff
        @x += @dx <=> 0
        @x = 0 if @x < 1
        @x = @window.width if @x > @window.width
      end
    end
    
    @dx *= 0.9 if @dx != 0 #slow down over time
    
    if @y < 1 and !@dead
      die(self)
    end
    
    if @dead
      if @gibbed
        if milliseconds - @gib_start < GIB_ANIM_INTERVAL * @anim_skin_gibbing.size
          @cur_skin_image = @anim_skin_gibbing[(milliseconds-@gib_start) / GIB_ANIM_INTERVAL]
          @cur_uniform_image = @anim_uniform_gibbing[(milliseconds-@gib_start) / GIB_ANIM_INTERVAL]
        end
      else
        case @direction
          when :left
            @angle = 3 if @angle == 0
          when :right
            @angle = -3 if @angle == 0
        end
        @angle *= 1.05 if @angle.abs < 90
      end
    else
      case @direction
        when :left
          @weapon_angle += weapon_angle_adjust unless (@weapon_angle + weapon_angle_adjust > 0 or @weapon_angle + weapon_angle_adjust < -175)
        when :right
          @weapon_angle -= weapon_angle_adjust unless (@weapon_angle - weapon_angle_adjust < 0 or @weapon_angle - weapon_angle_adjust > 175)
      end
      selected_weapon.update
    end
    
    take_damage(@poisoner, 1) if @poisoned and !@dead and @lifetime % 20 == 0
  end
  
  def update_colors(color_uniform, color_skin)
    @color_uniform, @color_skin = color_uniform, color_skin
  end
  
  def score_change(amount)
    @score += amount
  end
  
  def selected_weapon
    @weapons[@selected_weapon_index]
  end
  
  def human?
    @human
  end
  
  def height
    if @angle.abs < 80
      return @height
    else
      return @width
    end
  end
end